﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace InSys.Documents
{
    public partial class FormHiearachyDocumentView : Form
    {
        public FormHiearachyDocumentView()
        {
            InitializeComponent();
        }
        private MapTestLinqDataContext db;
        private TreeDocumentViewItem tree;

        internal MapTestLinqDataContext DataContext
        {
            get
            {
                return db;
            }
            set
            {
                db = value;
            }
        }



        public TreeNode FillTree(document doc)
        {
            tree = new TreeDocumentViewItem();
            tree.DataContext = db;
            tree.DeepRecurseInterrupt = 100; // наибольшее количество вызовов рекурсивных функций. Превышение, возможной зацикленностью
            tree.AddDocument(doc);
            treeView1.BeginUpdate();
            //treeView1.Nodes.Add(tree.rootTreeNode);
            foreach (TreeNode tn in tree.rootTreeNode.Nodes)
            {
                treeView1.Nodes.Add(tn);
            }
            treeView1.EndUpdate();
            return tree.rootTreeNode;
        }
    }

    internal struct DocumentViewItem
    {
        public document Document;
        public bool IsDefaultDocument;
    }


    internal class TreeDocumentViewItem
    {
        private static int iteration_count;
        private TreeDocumentViewItem parent;
        private TreeDocumentViewItem[] children;
        private DocumentViewItem item;
        private bool item_for_delete = false;
        private static MapTestLinqDataContext db;
        private TreeNode treeNode;
        private static int deepRecurseInterrupt;

        private class Direction
        {
            public document doc;
            public bool topDirection;
            public bool downDirection;
        }

        private List<Direction> directionList;

        private void setTopDirection(document doc)
        {
            if (null == doc) return;
            bool bFound = false;
            int len = directionList.Count;
            foreach (Direction item in directionList)
            {
                if (item.doc == doc)
                {
                    bFound = true;
                    item.topDirection = true;
                    break;
                }
            }
            if (bFound == false)
            {
                Direction newdir = new Direction { doc = doc, downDirection = false, topDirection = true };
                directionList.Add(newdir);
            }
        }

        private void setDownDirection(document doc)
        {
            if (null == doc) return;
            bool bFound = false;
            int len = directionList.Count;
            foreach (Direction item in directionList)
            {
                if (item.doc == doc)
                {
                    bFound = true;
                    item.downDirection = true;
                    break;
                }
            }
            if (bFound == false)
            {
                Direction newdir = new Direction { doc = doc, downDirection = true, topDirection = false };
                directionList.Add(newdir);
            }
        }

        private bool isTopDirection(document doc)
        {
            if (null == doc) return false;
            bool result = false;
            foreach (Direction item in directionList)
            {
                if (item.doc == doc)
                {
                    result = item.topDirection;
                    break;
                }
            }
            return result;
        }

        private bool isDownDirection(document doc)
        {
            if (null == doc) return false;
            bool result = false;
            foreach (Direction item in directionList)
            {
                if (item.doc == doc)
                {
                    result = item.downDirection;
                    break;
                }
            }
            return result;
        }


        public TreeNode rootTreeNode
        {
            get
            {
                return treeNode;
            }
        }

        public int DeepRecurseInterrupt
        {
            get
            {
                return deepRecurseInterrupt;
            }
            set
            {
                deepRecurseInterrupt = value;
            }
        }

        public MapTestLinqDataContext DataContext
        {
            get
            {
                return db;
            }
            set
            {
                db = value;
            }
        }

        static private List<TreeDocumentViewItem> listTreeItems;

        static private TreeDocumentViewItem rootTreeItem;

        private DocumentViewItem CreateItem(document doc, bool default_doc)
        {
            return new DocumentViewItem { Document = doc, IsDefaultDocument = default_doc };
        }

        /// <summary>
        /// Создать элемент и добавить его в список
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="doc"></param>
        /// <param name="default_doc"></param>
        /// <returns></returns>
        private TreeDocumentViewItem CreateTreeDocumentViewItem(TreeDocumentViewItem parent, document doc, bool default_doc)
        {
            if (null != parent)
            {
            }
            DocumentViewItem item = CreateItem(doc, default_doc);
            TreeDocumentViewItem result = new TreeDocumentViewItem();
            result.parent = parent;
            result.item = item;
            result.children = new TreeDocumentViewItem[] { };
            AddChild(parent, result, true);
            return result;
        }

        private DocumentViewItem CopyItem(DocumentViewItem item)
        {
            DocumentViewItem result = new DocumentViewItem { Document = item.Document, IsDefaultDocument = item.IsDefaultDocument };
            return result;
        }

        /// <summary>
        /// Добавить к списку дочерних элементов элемент child. 
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="child"></param>
        /// <param name="bAddToList">Добавить child в список</param> 
        private void AddChild(TreeDocumentViewItem parent, TreeDocumentViewItem child, bool bAddToList)
        {
            if (null == child) return;
            if (null != parent)
            {
                Array.Resize<TreeDocumentViewItem>(ref parent.children, parent.children.Length + 1);
                parent.children[parent.children.Length - 1] = child;
            }
            if (bAddToList && null != listTreeItems)
            {
                listTreeItems.Add(child);
            }
        }

        private TreeDocumentViewItem CopyTreeItem(TreeDocumentViewItem treeItem)
        {
            iteration_count++;
            if (iteration_count > deepRecurseInterrupt) return null;
            TreeDocumentViewItem newTreeItem = new TreeDocumentViewItem();
            TreeDocumentViewItem curTreeItem;
            newTreeItem.parent = rootTreeItem;
            newTreeItem.item = CopyItem(treeItem.item);
            newTreeItem.children = new TreeDocumentViewItem[] { };
            if (null != listTreeItems)
            {
                listTreeItems.Add(newTreeItem);
            }
            foreach (TreeDocumentViewItem itm in treeItem.children)
            {
                curTreeItem = CopyTreeItem(itm);
                curTreeItem.parent = newTreeItem;
                AddChild(newTreeItem, curTreeItem, true);
            }
            return newTreeItem;
        }

        private TreeDocumentViewItem findFirstTreeDocumentViewItem(document doc)
        {
            if (listTreeItems == null) return null;
            TreeDocumentViewItem result = null;
            foreach (TreeDocumentViewItem item in listTreeItems)
            {
                if (item.item.Document == doc)
                {
                    result = item;
                    break;
                }
            }
            return result;
        }

        private void RemoveMarkedForDeleteItems()
        {
            int length = listTreeItems.Count;
            int current_item = 0;
            TreeDocumentViewItem cur_item;
            while (current_item < length)
            {
                cur_item = listTreeItems.ElementAt(current_item);
                if (cur_item.item_for_delete)
                {
                    listTreeItems.RemoveAt(current_item);
                    length--;
                    continue;
                }
                current_item++;
            }
        }

        private void RemoveChild(TreeDocumentViewItem TreeItem, document child)
        {
            int child_pos = -1;
            for (int _cur = 0; _cur < TreeItem.children.Length; _cur++)
            {
                if (TreeItem.children[_cur].item.Document == child)
                {
                    child_pos = _cur;
                    break;
                }
            }
            if (child_pos == -1) return;
            for (int _cur = child_pos; _cur < TreeItem.children.Length - 1; _cur++)
            {
                TreeItem.children[_cur] = TreeItem.children[_cur + 1];
            }
            Array.Resize<TreeDocumentViewItem>(ref TreeItem.children, TreeItem.children.Length - 1);
        }

        private bool IsDocumentChildForParent(TreeDocumentViewItem parent, document doc)
        {
            bool result = false;
            foreach (TreeDocumentViewItem item in parent.children)
            {
                if (item.item.Document == doc)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }

        private void AddParent(document parent_doc, document current_doc, bool default_doc)
        {
            if (current_doc == null) return;
            // ищем первый попавшийся элемент для последующего копирования при необходимости
            TreeDocumentViewItem elementForCopy = findFirstTreeDocumentViewItem(current_doc);
            bool bCreatedElementForCopy = false;
            if (null == elementForCopy)
            {
                elementForCopy = CreateTreeDocumentViewItem(rootTreeItem, current_doc, false);
                bCreatedElementForCopy = true;
            }
            elementForCopy.item.IsDefaultDocument = default_doc;
            // просматриваем все существующие клоны пары <parent_doc, current_doc>
            if (null == parent_doc)
            {
                // в корень дерева добавляем без дополнительных проверок
                if (bCreatedElementForCopy)
                {
                    bCreatedElementForCopy = false;
                }
                else
                {
                    CopyTreeItem(elementForCopy);
                }
                return;
            }

            int nCnt = listTreeItems.Count;
            for (int _cnt = 0; _cnt < nCnt; _cnt++)
            {
                TreeDocumentViewItem item = listTreeItems[_cnt];

                if (item.parent != null && item.parent.item.Document == parent_doc && item.item.Document == current_doc)
                {
                    // найдено соответствие документа и родительского документа
                    item.item.IsDefaultDocument = default_doc;
                    continue; // документ найден. Продолжаем обход
                }
                else
                {
                    //item.item.IsDefaultDocument = false;

                    if (item.item.Document == parent_doc)
                    {
                        if (IsDocumentChildForParent(item, current_doc))
                        {
                            // документ уже есть в родительском документе. Ничего корректировать не нужно
                            item.item.IsDefaultDocument = default_doc;
                            continue;
                        }
                        // документа в родительском документе нет. Необходимо его добавить
                        if (bCreatedElementForCopy)
                        {
                            bCreatedElementForCopy = false;
                            AddChild(item, elementForCopy, false);
                        }
                        else
                        {
                            AddChild(item, CopyTreeItem(elementForCopy), false);
                        }
                        continue;
                    }
                    // отдельно существующие элементы не изменяем
                }
            }

        }

        /// <summary>
        /// Построить дерево для отображения в элементе управления
        /// </summary>
        private TreeNode BuildNodes()
        {
            TreeNode root = new TreeNode();
            root.Text = "\\";
            rootTreeItem.treeNode = root;
            BuildNodes(rootTreeItem);
            return root;
        }

        private void BuildNodes(TreeDocumentViewItem tree)
        {
            StringBuilder str;
            foreach (TreeDocumentViewItem item in tree.children)
            {
                if (null == item.treeNode)
                {
                    item.treeNode = new TreeNode();
                    item.treeNode.Tag = item.item.Document;
                    str = new StringBuilder();
                    if (item.item.IsDefaultDocument)
                    {
                        str.Append("(*)");
                    }
                    if (item.item.Document != null)
                    {
                        str.Append(item.item.Document.enterprise.Trim()).Append(" ");
                        str.Append(item.item.Document.kindDocument.ToString()).Append(" ");
                        str.Append(item.item.Document.number.Trim()).Append(" ");
                        str.Append(item.item.Document.date.ToString());
                    }
                    item.treeNode.Text = str.ToString();
                    tree.treeNode.Nodes.Add(item.treeNode);
                    BuildNodes(item);
                }
            }
        }

        public void AddDocument(document doc)
        {
            iteration_count = 0;
            if (null == listTreeItems)
            {
                listTreeItems = new List<TreeDocumentViewItem>();
            }
            else
            {
                listTreeItems.Clear();
            }
            if (null == rootTreeItem)
            {
                rootTreeItem = new TreeDocumentViewItem();
                rootTreeItem.parent = null;
                rootTreeItem.children = new TreeDocumentViewItem[] { };
                rootTreeItem.item.Document = null;
                rootTreeItem.item.IsDefaultDocument = false;
            }
            else
            {
                rootTreeItem.children = new TreeDocumentViewItem[] { };
            }
            directionList = new List<Direction>();
            // 1-й проход снизу вверх, затем вниз
            AddDocument(doc, true);

            foreach (TreeDocumentViewItem item in listTreeItems)
            {
                if (null != item.item.Document)
                {
                    if (item.item.Document == doc)
                    {
                        item.item.IsDefaultDocument = true;
                    }
                    else
                    {
                        item.item.IsDefaultDocument = false;
                    }
                }
            }
            treeNode = BuildNodes();
        }

        private void AddDocument(document doc, bool bFirst)
        {
            iteration_count++;
            if (iteration_count > deepRecurseInterrupt) return;
            document current_doc = doc;

            IEnumerable<document> child_docs = ClassWorkWithDB.getChildDocuments(db, doc);

            {
                IEnumerable<document> parent_docs = ClassWorkWithDB.getParentDocuments(db, doc);
                if (parent_docs.Count() != 0)
                {
                    foreach (document doc_item in parent_docs)
                    {
                        if (isTopDirection(doc_item)) continue;
                        setTopDirection(doc_item);
                        AddDocument(doc_item, false);
                        AddParent(doc_item, current_doc, bFirst);
                    }

                }
                else
                {
                    AddParent(null, current_doc, bFirst);
                }
            }
            foreach (document doc_item in child_docs)
            {
                if (isDownDirection(doc_item)) continue;
                setDownDirection(doc_item);
                AddParent(current_doc, doc_item, false);
                AddDocument(doc_item, false);
            }
        }
    }
}
